<template>
  <div class="wapper">
    <div class="flex  flex-center pointer" @click="setImgGap">
      <img src=".././images/sx.png" class="sx">
      <span class="text">图片不好看？换一张吧</span>
    </div>
    <div class="main">
      <p style="font-weight: bold;" :style="{ color: textColor }" class="text-1">{{ text }}</p>
      <div class="img-wapper" :style="{ backgroundImage: `url(${imgArr[randomImgIndex]})` }" ref="imgWapperRef">
        <div class="img-block"
          :style="{ zIndex: 10, left: `${sliderLeft}px`, top: `${gapTop}px`, backgroundImage: `url(${imgArr[randomImgIndex]})`, backgroundPosition: `-${gapLeft}px -${gapTop}px`, transition }">
        </div>
        <div class="img-gap" ref="imgGapRef" :style="{ left: `${gapLeft}px`, top: `${gapTop}px` }"></div>
      </div>
      <div class="slider">
        <button class="slider-btn" @mousedown="startMove" ref="sliderRef"
          :style="{ left: `${sliderLeft}px`, transition }"></button>
        <span class="slider-text" :style="{ opacity: isMove || isVerify ? 0 : 1 }">拖动滑块完成拼图</span>
      </div>
    </div>
  </div>
</template>
  
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import t1 from '.././images/t1.png';
import t2 from '.././images/t2.png';
import t3 from '.././images/t3.png';
import t4 from '.././images/t4.png';
import t5 from '.././images/t5.png';

const imgArr = [t1, t2, t3, t4, t5]; // 图片
const text = ref('请完成图片验证'); // 文字
const textColor = ref('#f60'); // 验证的文字颜色
const randomImgIndex = ref(-1); // 随机图片索引
const imgWapperRef = ref<HTMLDivElement | null>(null); // 图片容器
const imgGapRef = ref<HTMLDivElement | null>(null); // 图片缺口容器
const gapTop = ref<number>(0); // 缺口top
const gapLeft = ref<number>(0); // 缺口left
const sliderLeft = ref<number>(0); // 滑块left
const sliderRef = ref<HTMLDivElement | null>(null); // 滑块容器
const isMove = ref(false); // 是否移动
const isVerify = ref(false); // 是否验证成功
const transition = ref('none')
let maxTop = 0; // 最大top
let maxLeft = 0; // 最大left
const DEVIATION = 2; // 偏差
const MAX_VERIFICATIONS = 5; // 最大验证次数
let verifications = 0; // 记录验证的次数
// 获取随机图片索引
const getRandomIndex = () => {
  let randomIndex = Math.floor(Math.random() * imgArr.length)
  while (randomIndex === randomImgIndex.value) {
    randomIndex = Math.floor(Math.random() * imgArr.length)
  }
  randomImgIndex.value = randomIndex
}

onMounted(() => {
  maxTop = imgWapperRef.value?.offsetHeight! - imgGapRef.value?.offsetHeight!
  maxLeft = imgWapperRef.value?.offsetWidth! / 2 - imgGapRef.value?.offsetWidth! // 保证至少大于一半的位置
  setImgGap()
})
// 上次移动的距离
let perSliderLeft = 0

// 获取缺口的位置
const setImgGap = () => {
  getRandomIndex();
  isVerify.value = false;
  verifications = 0;
  textColor.value = '#f60'
  text.value = '请完成图片验证'
  sliderLeft.value = 0
  perSliderLeft = 0
  gapTop.value = Math.random() * maxTop;
  gapLeft.value = Math.random() * maxLeft + imgWapperRef.value?.offsetWidth! / 2;
}


let startMove = (ev: MouseEvent) => {
  // 防止成功后重复验证
  if (isVerify.value) return
  transition.value = 'none'
  let { left } = sliderRef.value?.getBoundingClientRect() as DOMRect; // 元素到左边视口的距离

  let handleMove = (e: MouseEvent) => {
    isMove.value = true;
    // 滑块的位置
    sliderLeft.value = e.clientX - left - ev.offsetX + perSliderLeft;
    let maxLeftGap = imgWapperRef.value?.offsetWidth! - imgGapRef.value?.offsetWidth!
    if (sliderLeft.value < 0) {
      // 滑块最小位置
      sliderLeft.value = 0
    } else if (sliderLeft.value > maxLeftGap) {
      // 滑块最大位置
      sliderLeft.value = maxLeftGap
    }
  };

  let gapDiff = () => {
    // 正负偏差5为通过验证
    if (Math.abs(gapLeft.value - sliderLeft.value) <= DEVIATION) {
      isVerify.value = true;
      textColor.value = '#0f0'
      text.value = '验证成功'
    } else {
      transition.value = 'all .5s'
      perSliderLeft = sliderLeft.value = 0;
      text.value = '验证失败'
      textColor.value = 'red'
      ++verifications;
      if(verifications>=MAX_VERIFICATIONS){
        setImgGap();
      }
    }

  }

  let handleMouseUp = (e: MouseEvent) => {
    if (isMove.value) {
      // 更新上次移动的距离
      perSliderLeft = sliderLeft.value;
      isMove.value = false;
      gapDiff()
    }
    // 解绑事件
    document.removeEventListener("mousemove", handleMove);
    document.removeEventListener("mouseup", handleMouseUp);
  };
  // 移动
  document.addEventListener("mousemove", handleMove);
  // 松开
  document.addEventListener("mouseup", handleMouseUp);
};

</script>
<style lang="scss" scoped>
.wapper {
  width: 280px
}

.sx {
  width: 26px;
  height: 26px;
}

.text {
  font-size: 16px;
  color: rgb(126, 57, 218);
  font-weight: bolder;
  cursor: pointer;
  user-select: none;
  margin-left: 10px;
}

.text-1 {
  text-align: center;
}

.main {
  box-sizing: border-box;
  padding: 15px;
  border: 1px solid #adadad;
  box-shadow: 0px 0px 2px #adadad;
  border-radius: 15px;
  margin-top: 20px;
  box-sizing: border-box;
}

.img-wapper {
  width: 100%;
  height: 200px;
  background-repeat: no-repeat;
  position: relative;
}

.img-block,
.img-gap {
  width: 50px;
  height: 50px;
  position: absolute;
  background-color: white;
}

.slider {
  width: 100%;
  height: 30px;
  margin: auto;
  margin-top: 15px;
  background-color: #ddd;
  border-radius: 15px;
  position: relative;
  text-align: center;
  line-height: 30px;
  font-size: 14px;
  font-weight: 200;
}

.slider-text {
  opacity: 1;
}

.slider-btn {
  background: white url('.././images/yz.png') no-repeat;
  position: absolute;
  background-size: 100%;
  border-radius: 50%;
  border: 0;
  width: 40px;
  height: 40px;
  cursor: pointer;
  top: -5px;
  z-index: 1;
}

.flex {
  display: flex;
}

.flex-center {
  align-items: center;
  justify-content: center;
}

.pointer {
  cursor: pointer;
}
</style>